package de.hs.rm.UJAdapter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

import javax.management.InstanceNotFoundException;
import javax.management.JMX;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerDelegateMBean;
import javax.management.MalformedObjectNameException;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.upnp.UPnPDevice;
import org.osgi.service.upnp.UPnPService;

import de.hs.rm.UJAdapter.event.impl.jmx.UJRegisterNotificationListener;
import de.hs.rm.UJAdapter.factory.UJDeviceFactory;
import de.hs.rm.UJAdapter.factory.UJServiceFactory;
import de.hs.rm.UJAdapter.impl.UJDevice;
import de.hs.rm.UJAdapter.impl.UJService;
import de.hs.rm.UJAdapter.util.StringGenerationUtil;

public class UJAdapter {

	private Dictionary subDeviceRegistrations = new Hashtable();
	private JMXConnector jmxConnector = null;
	private JMXServiceURL serviceUrl = null;
	private UJDevice agentRootDevice = null;
	private ArrayList<UJDevice> agentSubDevices = null;
	private ServiceRegistration serviceRegistration;
	private NotificationListener registerListener = null;
	private MBeanServerConnection mBeanServerConnection = null;
	private ObjectName delegateObjName = null;
	
	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void startAdapter(BundleContext context) throws Exception {
		
		createUPnPDevice(context);	
	}
	
	public void removeUPnPDevice(BundleContext context) throws InstanceNotFoundException, ListenerNotFoundException, IOException
	{
		mBeanServerConnection.removeNotificationListener(delegateObjName, registerListener);
		Enumeration<String> deviceUDNs = subDeviceRegistrations.keys();
		String deviceUDN = "";
		while (deviceUDNs.hasMoreElements())
		{
			deviceUDN = deviceUDNs.nextElement();
			serviceRegistration  = (ServiceRegistration)subDeviceRegistrations.get(deviceUDN);
	    	UJDevice dev = (UJDevice) context.getService(serviceRegistration.getReference());
	    	if(serviceRegistration != null)
	    		serviceRegistration.unregister();
    		subDeviceRegistrations.remove(deviceUDN);
		}
	}
	
	public void createUPnPDevice(BundleContext context) throws Exception{
		
		System.out.println("Starting UPnP to JMX Adapter...");
		String host = "localhost";
        int port = 9999;
        String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
        
        
		try {
			
			//connect to local MBeanServer
			serviceUrl = new JMXServiceURL(url);
			jmxConnector = JMXConnectorFactory.connect(serviceUrl, null);
		    mBeanServerConnection = jmxConnector.getMBeanServerConnection();
		    //MBeanServerDelegate Object Name 
		    delegateObjName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
            //get MBeanServerDelegate
            MBeanServerDelegateMBean delegateProxyMBean = JMX.newMBeanProxy(mBeanServerConnection, delegateObjName, 
            		MBeanServerDelegateMBean.class, true);
            
//            ObjectName pizzaObjName = new ObjectName("de.hs.rm.jmx:type=Pizzeria");
//			MBeanInfo pizzaMBeanInfo = mBeanServerConnection.getMBeanInfo(pizzaObjName);
//			MBeanAttributeInfo[] pizzaMBeanAttributes = pizzaMBeanInfo.getAttributes();
//			Object pizzaObj = mBeanServerConnection.getAttribute(pizzaObjName, "ExamplePizza");
//			//System.out.print(pizzaObj.toString());
//			UPnPService pizzaService = UJServiceFactory.createService(delegateProxyMBean, pizzaMBeanInfo, mBeanServerConnection, pizzaObjName);
          
//          for (MBeanAttributeInfo pA : pizzaMBeanAttributes)
//          {
//        	  Object pizzaObj = mBeanServerConnection.getAttribute(pizzaObjName, pA.getName());
//              System.out.print(pizzaObj.toString());        	  
//          }
          
          
//          MBeanInfo hwMBeanInfo = mBeanServerConnection.getMBeanInfo(hWobjName);	    
		    
//            ObjectName hWobjName = new ObjectName("de.hs.rm.jmx:type=HelloWorld");
//            MBeanInfo hwMBeanInfo = mBeanServerConnection.getMBeanInfo(hWobjName);
//            MBeanAttributeInfo[] hwMBeanAttributes = hwMBeanInfo.getAttributes();
//            MBeanOperationInfo[] hwMBeanOperations = hwMBeanInfo.getOperations();
////            System.out.println(hwMBeanAttributes[0].getType());
////            System.out.println(getJavaDataTypeFromString(hwMBeanAttributes[0].getType()));
////            
////            for(MBeanOperationInfo hwOp : hwMBeanOperations)
////            {
////            	System.out.println("Name: " + hwOp.getName());
////            	System.out.println("Type: " + hwOp.getReturnType());
////            }
//            System.out.println(mBeanServerConnection.getAttribute(hWobjName,hwMBeanAttributes[0].getName()));
//            Object result = mBeanServerConnection.invoke(hWobjName,
//					"printName",
//					null,
//					null);
            
//            ObjectName hwObjName = new ObjectName("de.hs.rm.jmx:type=HelloWorld");
//            UJNotificationListener notificationListener = new UJNotificationListener();
//            mBeanServerConnection.addNotificationListener(hwObjName, notificationListener, null, null);
//            MBeanInfo hwMBeanInfo = mBeanServerConnection.getMBeanInfo(hwObjName);
//            MBeanNotificationInfo[] mBeanNotifications = hwMBeanInfo.getNotifications();
//            MBeanNotificationInfo tmp = mBeanNotifications[0];
            		    
            
            //get MBean Domains
            String[] domains = mBeanServerConnection.getDomains();
            String[] subDeviceUDNs = new String[domains.length]; //extra array, avoiding confusion with domains
            String rootDeviceUDN = StringGenerationUtil.getRootDeviceUDNfromServiceURL(serviceUrl);
            
            //create SubDevice per MBean Domain
            for (int i = 0; i < domains.length; i++)
            {
            	ObjectName searchObjName = new ObjectName(domains[i] + ":*");
            	Set<ObjectName> set = mBeanServerConnection.queryNames(searchObjName, null);
            	Iterator<ObjectName>  it = set.iterator();
            	
            	// create SubDevice
            	subDeviceUDNs[i] =  StringGenerationUtil.getUDNfromDomainName(domains[i]);
            	UJDevice agentSubDevice =
            			UJDeviceFactory.createSubDevice(context, delegateProxyMBean, subDeviceUDNs[i] , rootDeviceUDN);
            	
            	//create UPNP Services for all MBeans in Domain
            	UPnPService[] services = new UJService[set.size()];
            	int j = 0;
            	while (it.hasNext())
            	{
            		ObjectName objName = it.next();
            		MBeanInfo mBeanInfo = mBeanServerConnection.getMBeanInfo(objName);
            		services[j] = UJServiceFactory.createService(agentSubDevice, delegateProxyMBean, mBeanInfo, mBeanServerConnection, objName);
            		j++;
            	}
            	agentSubDevice.setServices(services);            	
            	//register UPnPSubDevice
                Dictionary dictSub = agentSubDevice.getDescriptions(null);
                serviceRegistration = context.registerService(
        				UPnPDevice.class.getName(),
        				agentSubDevice,
        				dictSub
        		);
                if(serviceRegistration != null)
                	subDeviceRegistrations.put(subDeviceUDNs[i],serviceRegistration);
            }
            
            //Create Util Device (use info from MBeanServerDelegate)
//            String utilDeviceUDN = "uuid:JMX:Utils";
//            String[] childrenUDN = new String[domains.length + 1];
//            childrenUDN[0] = utilDeviceUDN;
//            for(int i = 0; i < domains.length; i++)
//            {
//            	childrenUDN[i+1] = domains[i];
//            }
//            UJDevice agentUtilDevice =
//            		UJDeviceFactory.createSubDevice(context, delegateProxyMBean, utilDeviceUDN , "uuid:JMX:MBeanServer:" + serviceUrl);
//            UPnPService[] notficationServices = {UJServiceFactory.createNotificationService(agentUtilDevice, delegateProxyMBean, mBeanServerConnection, delegate, "Registration")};
//            agentUtilDevice.setServices(notficationServices);
//            
//            Dictionary dictUtils = agentUtilDevice.getDescriptions(null);
//            serviceRegistration = context.registerService(
//                UPnPDevice.class.getName(),
//                agentUtilDevice,
//                dictUtils
//            );
            
            //Register MBeanServerNotificatio LIstener for MBean (de)registration
            registerListener = new UJRegisterNotificationListener();
            ((UJRegisterNotificationListener)registerListener).setListenerProps(context, subDeviceRegistrations, delegateProxyMBean, mBeanServerConnection, rootDeviceUDN);
            //registerListener = new UJEasyRegisterNotificationListener(this, context);
            
            mBeanServerConnection.addNotificationListener(delegateObjName, registerListener, null, null);
            
            //Create Root Device (use info from MBeanServerDelegate)
            agentRootDevice =
            		UJDeviceFactory.createRootDevice(context, delegateProxyMBean, rootDeviceUDN , subDeviceUDNs);
                      
            //register Root Device
            Dictionary dictRoot = agentRootDevice.getDescriptions(null);
            serviceRegistration = context.registerService(
                UPnPDevice.class.getName(),
                agentRootDevice,
                dictRoot
            );
            if(serviceRegistration != null)
            	subDeviceRegistrations.put(rootDeviceUDN,serviceRegistration);           
            
        } catch (IOException e) {
            System.err.println(e.getMessage());
        } catch (MalformedObjectNameException e) {
            System.err.println(e.getMessage());
        } catch (NullPointerException e) {
            System.err.println(e.getMessage());
        } 

	}
	
	public void stopAdapter(BundleContext context) throws Exception {
		
        try {
			jmxConnector.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
        removeUPnPDevice(context);
		System.out.println("Stopping UPnP to JMX Adapter...");
	}
}
